import numpy
import argparse
import cv2
import os,sys,pathlib

try:
    input_file = sys.argv[1]
except:
    input_file = '../test.jpg'

def Laplacian(origin):
#Laplacian算子是n维欧几里德空间中的一个二阶微分算子，定义为梯度grad的散度div。可使用运算模板来运算这定理定律。Laplacian算子对噪声比较敏感，所以图像一般先经过平滑处理，因为平滑处理也是用模板进行的，所以，通常的分割算法都是把Laplacian 算子和平滑算子结合起来生成一个新的模板。
    lap = cv2.Laplacian(origin,cv2.CV_64F)
    result = numpy.uint8(numpy.absolute(lap))

    return result
    
def Sobel(origin):
#sobel非线性滤波采用梯度模的近似方式提取边缘,锐化图像,可以分别计算水平和垂直方向上的灰阶突变。
    sobelX = cv2.Sobel(origin,cv2.CV_64F,1,0)
    sobelY = cv2.Sobel(origin,cv2.CV_64F,0,1)
    resultX = numpy.uint8(numpy.absolute(sobelX))
    resultY = numpy.uint8(numpy.absolute(sobelY))
    result = cv2.bitwise_or(resultX,resultY)

    return result

def Canny(origin,min_thre,max_thre):
#Canny边缘检测可以相对有效的连接断裂的边缘，需要指定一个最大最小灰阶值组成区间，结果生成的是二值图像。
    canny = cv2.Canny(origin,min_thre,max_thre)
    result = numpy.uint8(numpy.absolute(canny))
    
    return result
    
def Contours(origin):
#cv2.findContours()函数接受的参数为二值图，即黑白的（不是灰度图），所以读取的图像要先转成灰度的，再转成二值图
    ret,binary = cv2.threshold(origin,127,255,cv2.THRESH_BINARY)
    #contours,hierachy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
    thresh,contours,hierachy = cv2.findContours(binary,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    approx = cv2.approxPolyDP(cnt,epsilon,True)
    hull = cv2.convexHull(cnt)
    x,y,w,h = cv2.boundingRect(cnt)
    min_rect = cv2.minAreaRect(cnt)
    (x,y),radius = cv2.minEnclosingCircle(cnt)

    return contours

def Contours_from_Canny(origin):
#zhuanlan.zhihu.com/p/38739563
#https://github.com/Yonv1943/Python/blob/master/Demo/TUTO_edge_detection.py
    canny = Canny(origin,128,255)
    thresh,contours,hierachy = cv2.findContours(canny,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
#第一个是传入的二值图，不等于0的像素将参与等高线的计算
#第二个决定hierarchy 采取什么样的格式输出，具体格式看→Contours Hierarchy
#第三个决定是否输出精简过的 contours，具体格式看→Contours : Getting Started
#hierarchy是一个三维数组，它储存了等高线的情况
    
    def draw_approx_hull_polygon(img,cnts):
        img = np.copy(img)
        cv2.drawContours(img,cnts,-1,(255,0,0),2) #blue
        #epsilon是近似轮廓的最小边长
        epsilon = img.shape[0]/32

        #贴合轮廓(近似多边形) cv2.approxPolyDP()
        #包围轮廓（船壳多边形）cv2.convexHull()
        approxes = [cv2.approxPolyDP(cnt, epsilon,True) for cnt in cnts]
        cv2.polylines(img,approxes,True,(0,255,0),2) #green

        hulls = [cv2.convexHull(cnt) for cnt in cnts]
        cv2.polylines(img,hulls,True,(0,0,255),2) #red

    
        return img

    def draw_min_rect_circle(img,cnts):
        img = np.copy(img)

        for cnt in cnts:
            x,y,w,h = cv2.boundingRect(cnt)
            cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2) #blue

            min_rect = cv2.minAreaRect(cnt)
            min_rect = np.int0(cv2.boxPoints(min_rect))
            cv2.drawContours(img,[min_rect],0,(0,255,0),2) #green
            
            (x,y),radius = cv2.minEnclosingCircle(cnt)
            center, radius = (int(x), int(y)), int(radius)
            img = cv2.circle(img,center,radius,(0,0,255),2) #red
        return img
    result_1 = draw_approx_hull_polygon(canny,contours)
    result_2 = draw_min_rect_circle(canny,contours)

    result = numpy.hstack([result_1,result_2])
    #x,y,w,h = cv2.boundingRect(cnt)
    #min_rect = cv2.minAreaRect(cnt)
    #(x,y),radius = cv2.minEnclosingCircle(cnt)

    return result

def DrawContours(origin,contours):
    result = cv2.drawContours(origin,contours,-1,(0,0,255),3)

    return result

def detect_edge_0(origin):
    gray = cv2.cvtColor(origin,cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray,100,150)
    minLineLenght = 200
    maxLineGap = 100
    lines = cv2.HoughLinesP(edges,1,np.pi/180,30,minLineLength,maxLineGap)
    for line in lines:
        x1,y1,x2,y2 = line[0]
        result = cv2.line(origin,(x1,y1),(x2,y2),(0,255,0),2)
        print(line)

    for theta in lines:
        a = np.cos(theta)
        b = np.sin(theta)
        print(a)
        print(b)

    return result


def detect_edge_1(origin):
    gray = cv2.cvtColor(origin,cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray,100,150)
    edges = cv2.dilate(edges,cv2.getStructuringElement(cv2.MORPH_RECT,(3,3)))
    minLineLenght = 50
    maxLineGap = 10
    lines = cv2.HoughLinesP(edges,1,np.pi/180,30,minLineLength,maxLineGap)
    for line in lines:
        x1,y1,x2,y2 = line[0]
        result = cv2.line(origin,(x1,y1),(x2,y2),(0,255,0),2)
        print(line)

    for theta in lines:
        a = np.cos(theta)
        b = np.sin(theta)
        print(a)
        print(b)

    return result

def detect_edge_2(origin):
#https://docs.opencv.org/4.0.1/dc/d0d/tutorial_py_features_harris.html
    gray = cv2.cvtColor(origin,cv2.COLOR_BGR2GRAY)
    gray = np.float32(gray)
    dest = cv2.cornerHarris(gray,2,3,0.04)
    dest = cv.dilate(dest,None)
    result = origin[dest>0.01*dst.max()] = [0,0,255]

    return result

def detect_edge_3(origin):
#https://docs.opencv.org/4.0.1/dc/d0d/tutorial_py_features_harris.html
    gray = cv2.cvtColor(origin,cv2.COLOR_BGR2GRAY)
    gray = np.float32(gray)
    # find Harris corners
    dest = cv2.cornerHarris(gray,2,3,0.04)
    dest = cv.dilate(dest,None)
    ret,dest = cv.threshold(dest,0.01*dest.max(),255,0)
    dest = np.uint8(dest)
    # find centroids
    ret,labels,stats,centroids = cv2.connectedComponentsWithStats(dest)
    # define the criteria to stop and refine the corners
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.CRITERIA_MAX_ITER, 100, 0.001)
    result = cv2.cornerSubPix(gray,np.float32(centroids),(5,5),(-1,-1),criteria)

    return result


def find_max_position(image):
    indices = np.where(image != [0])
    coordinates = zip(indices[0],indices[1])

    return coorinates

def find_edge(origin):
    [height,width,channels] = origin.shape
    trace = []
    start_x = 0
    start_y = 0
    gray = origin[:,:,1]
    for h in range(height):
        for w in range(width):
            if (gray[h,w] > 128):
                gray[h,w] = 255
            else:
                gray[h,w] = 0
    class getoutofloop(Exception): pass
    try:
        for h in range(height - 2):
            for w in range(width -2):
                if gray[h,w] == 0:
                    start_x = w
                    start_y = h
                    raise getoutofloop
    except getoutofloop:
        pass
    print("Start Point(%d %d)"%(start_x,start_y))
    trace.append([start_x,start_y])

    neighbor = [
        [-1,-1],
        [0,-1],
        [1,-1],
        [1,0],
        [1,1],
        [0,1],
        [-1,1],
        [-1,0]]
    neighbor_len = len(neighbor)

    i = 0
    cur_x = start_x + neighbor[i][0]
    cur_y = start_y + neighbor[i][1]

    is_contour_point = 0

    try:
        while not ((cur_x == start_x) and (cur_y == start_y)):
            is_contour_point = 0
            while is_contour_point == 0:
                if gray[cur_y,cur_x] == 0:
                    is_contour_point = 1
                    trace.append([cur_x,cur_y])
                    i -= 2
                    if i<0:
                        i += neighbor_len
                else:
                    i += 1
                    if i >= neighbor_len:
                        i -= neighbor_len
                cur_x = cur_x + neighbor[i][0]
                cur_y = cur_y + neighbor[i][1]
    except:
        print("error")

    for i in range(len(trace)-1):
        cv2.line(origin,(trace[i][0],trace[i][1]),(trace[i+1][0],trace[i+1][1]),(0,0,255),3)
        cv2.imshow("img",origin)
        cv2.waitKey(10)
    cv2.rectangle(origin,(start_x,start_y),(start_x+20,start_y+20),(255,0,0),2)
    cv2.imshow("img",origin)
    cv2.waitKey(0)
    cv2.destroyWindow("img")

    return

if __name__ == '__main__':
    image = cv2.imread(input_file)
    find_edge(image)
    gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    lap = Laplacian(gray)
    sobel = Sobel(gray)
    canny = Canny(gray,30,150)
    sobel_canny = Canny(sobel,30,150)
    ret,edge_output = cv2.threshold(canny,80,150,cv2.THRESH_BINARY_INV)

    #cv2.imshow("Edge detection by Laplacian",numpy.hstack([lap,gray]))
    cv2.imwrite('1_edge_by_laplacian.jpg',numpy.hstack([gray,lap]))

    #cv2.imshow("Edge detection by Sobel",numpy.hstack([sobel,gray]))
    cv2.imwrite('1_edge_by_sobel.jpg',numpy.hstack([gray,sobel]))

    #cv2.imshow("Edge detection by Canny",numpy.hstack([canny,gray]))
    cv2.imwrite('1_edge_by_canny.jpg',numpy.hstack([gray,canny]))
    cv2.imwrite('1_edge_by_canny_threshold.jpg',edge_output)

    cv2.imwrite('1_edge_by_sobel_and_canny.jpg',numpy.hstack([canny,sobel_canny]))
    if (cv2.waitKey(0)==27):
        cv2.destroyAllWindows()
